﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using SalesPoint.Commands;
using SalesPoint.Helpers;
using SalesPoint.Model;
using SalesPoint.Security;
using Xtensive.Orm;
using Xtensive.Orm.Security;

namespace SalesPoint.ViewModels
{
  public abstract class GenericListViewModel<T> : WorkspaceViewModel where T : class, IEntity
  {
    private DelegateCommand addItemCommand;
    private DelegateCommand editItemCommand;
    private T item;
    private ObservableCollection<T> items;
    private DelegateCommand removeItemCommand;

    public ObservableCollection<T> Items
    {
      get
      {
        if (items != null)
          return items;
        items = GetItems();
        Item = items.FirstOrDefault();
        return items;
      }
    }

    public T Item
    {
      get { return item; }
      set
      {
        if (ReferenceEquals(item, value))
          return;
        item = value;
        OnPropertyChanged("Item");
      }
    }

    #region Commands

    public ICommand AddItemCommand
    {
      get
      {
        if (addItemCommand != null)
          return addItemCommand;

        addItemCommand = new DelegateCommand(AddItem, CanEdit);
        return addItemCommand;
      }
    }

    public ICommand EditItemCommand
    {
      get
      {
        if (editItemCommand != null)
          return editItemCommand;

        editItemCommand = new DelegateCommand(EditItem, CanEdit);
        return editItemCommand;
      }
    }

    public ICommand RemoveItemCommand
    {
      get
      {
        if (removeItemCommand != null)
          return removeItemCommand;

        removeItemCommand = new DelegateCommand(RemoveItem, CanEdit);
        return removeItemCommand;
      }
    }

    #endregion

    #region Command implementation

    private void AddItem()
    {
      GenericItemViewModel<T> viewModel;
      Window view;
      try {
        viewModel = GetItemViewModel();
        view = GetItemView();
      }
      catch (NotImplementedException) {
        var e = Session.GetImpersonationContext().Principal as Employee;
        string message = string.Format("You logged in as {0} {1}, {2}, {3}.\nYou have permission to add new item.",
          e.FirstName, e.LastName, e.Title, e.Address.City);
        MessageBox.Show(message, "Sorry, this is temporary stub", MessageBoxButton.OK, MessageBoxImage.Information);
        return;
      }
      view.Owner = Application.Current.MainWindow;
      view.Title = string.Format("{0} properties", typeof (T).Name);
      view.WindowStartupLocation = WindowStartupLocation.CenterScreen;

      bool result;
      using (var ts = Session.OpenTransaction()) {
        T @new = CreateItem();
        Items.Add(@new);
        Item = @new;
        var dataGrid = DataGridHelper.GetVisualChild<DataGrid>(View);
        DataGridHelper.GetRow(dataGrid, items.Count - 1).Focus();

        view.DataContext = viewModel;
        bool? dialogResult = view.ShowDialog();
        result = dialogResult.HasValue && dialogResult.Value;
        if (result)
          ts.Complete();
        else {
          items.Remove(@new);
          Item = Items.LastOrDefault();
        }
      }
      if (result)
        Session.SaveChanges();
      Session.NotifyChanged();
    }

    private bool CanEdit()
    {
      var securityContext = Session.GetImpersonationContext();
      if (securityContext == null)
        return false;
      return securityContext.Permissions.Contains<Permission<T>>(p => p.CanWrite);
    }

    protected virtual void EditItem()
    {
      GenericItemViewModel<T> viewModel = null;
      Window view;
      try {
        viewModel = GetItemViewModel();
        view = GetItemView();
      }
      catch (NotImplementedException) {
        var e = Session.GetImpersonationContext().Principal as Employee;
        string message = string.Format("You logged in as {0} {1}, {2}, {3}.\nYou have permission to edit item.",
          e.FirstName, e.LastName, e.Title, e.Address.City);
        MessageBox.Show(message, "Sorry, this is temporary stub", MessageBoxButton.OK, MessageBoxImage.Information);
        return;
      }

      view.Owner = Application.Current.MainWindow;
      view.Title = string.Format("{0} properties", typeof (T).Name);
      view.DataContext = viewModel;
      view.WindowStartupLocation = WindowStartupLocation.CenterScreen;

      bool result;
      using (var ts = Session.OpenTransaction()) {
        bool? dialogResult = view.ShowDialog();
        result = dialogResult.HasValue && dialogResult.Value;
        if (result)
          ts.Complete();
      }
      if (result)
        Session.SaveChanges();
      Session.NotifyChanged();
    }

    protected virtual void RemoveItem()
    {
      var e = Session.GetImpersonationContext().Principal as Employee;
      string message = string.Format("You logged in as {0} {1}, {2}, {3}.\nYou have permission to remove item.",
        e.FirstName, e.LastName, e.Title, e.Address.City);
      MessageBox.Show(message, "Sorry, this is temporary stub", MessageBoxButton.OK, MessageBoxImage.Information);
      return;

      message =
        string.Format(
          "Confirm deletion of {0}",
          Item);
      var result = MessageBox.Show(message, "SalesPoint", MessageBoxButton.OKCancel,
                                                MessageBoxImage.Question);
      if (result != MessageBoxResult.OK)
        return;

      using (var ts = Session.OpenTransaction()) {
        Item.Remove();
        ts.Complete();
      }
      Session.SaveChanges();
      Items.Remove(Item);
      Session.NotifyChanged();
    }

    #endregion

    protected abstract T CreateItem();

    protected abstract Window GetItemView();

    protected virtual GenericItemViewModel<T> GetItemViewModel()
    {
      return new GenericItemViewModel<T>(Session, Item);
    }

    protected virtual ObservableCollection<T> GetItems()
    {
      return new ObservableCollection<T>(Session.Query.All<T>());
    }

    protected GenericListViewModel(Session session, string displayName)
      : base(session, displayName)
    {
    }
  }
}